package org.lex.swing.graph;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import org.lex.awt.Colors;
import org.lex.utils.Pair;


/**
 * ֻ�ṩ��Ļ��Ƶķ���
 * 
 * @author Daniel Liu
 */
public class BasicProgressGraph {
	public static final int FORE_MODE = 0;
	public static final int BACK_MODE = 1;
	// -----------------------------------
	private static final int BORDER_SIZE = 1;
	static final int WIDTH = 200;
	static final int HEIGHT = 200;
	// ===================================
	private BufferedImage image = null;
	private int rowHeight = 27;
	private int rowGap = 3;
	// -----------------------------------
	private Pair<Color, Color> fores = Colors.Diskeeper.BLUE;
	private Pair<Color, Color> backs = Colors.Diskeeper.WHITE;
	// -----------------------------------
	private Color background = new Color(236, 233, 216);
	protected Color borderColor = new Color(172, 168, 153);
	// -----------------------------------
	private int rowCount = 0;

	public BasicProgressGraph() {
		this(WIDTH, HEIGHT);
	}

	public BasicProgressGraph(int width, int height) {
		this.rowCount = height / (rowHeight + rowGap + 2 * BORDER_SIZE);
		this.setSize(width, height);
	}

	/***************************************************************************
	 * Derived Getters
	 **************************************************************************/
	public int getRowCount() {
		return this.rowCount;
	}

	public BufferedImage getImage() {
		return this.image;
	}

	public int getHeight() {
		return image.getHeight();
	}

	public int getWidth() {
		return image.getWidth();
	}

	public int getRowWidth() {
		return image.getWidth() - 2 * BORDER_SIZE;
	}

	/***************************************************************************
	 * Size Getters
	 **************************************************************************/
	public int getRowHeight() {
		return this.rowHeight;
	}

	public int getRowGap() {
		return rowGap;
	}

	protected void setSize(int width, int height) {
		if (null == image || width != getWidth() || height != getHeight())
			image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		this.initializeImage(image);
	}

	/***************************************************************************
	 * Color Getters
	 **************************************************************************/
	public Color getBackground() {
		return background;
	}

	public void setBackground(Color bg) {
		if (null == bg || bg.equals(this.getBackground()))
			return;
		this.background = bg;
		int height = image.getHeight();
		int width = image.getWidth();
		int totalRowHeight = rowHeight + rowGap + 2 * BORDER_SIZE;
		rowCount = height / totalRowHeight;
		Graphics2D g2 = (Graphics2D) image.createGraphics();
		Rectangle rect = new Rectangle();
		for (int row = 0; row < rowCount; row++) {
			// draw gap
			g2.translate(0, rowHeight + 2 * BORDER_SIZE);
			rect.setBounds(0, 0, width, rowGap);
			g2.setColor(background);
			g2.fill(rect);
			g2.translate(0, rowGap);
		}
		// fill rest with background
		rect.setBounds(0, 0, width, height - rowCount * totalRowHeight);
		g2.setColor(background);
		g2.fill(rect);
		g2.dispose();
	}

	public Color getBorderColor() {
		return borderColor;
	}

	/***************************************************************************
	 * Main Paint Methods
	 **************************************************************************/
	public void fillBackground(int row, float from, float to) {
		fill(BACK_MODE, row, from, to);
	}

	public void fillForeground(int row, float from, float to) {
		fill(FORE_MODE, row, from, to);
	}

	/**
	 * @param mode
	 *            ʹ��ǰ��ɫ���Ǳ���ɫģʽ
	 * @param row
	 *            �С����row>=getRowCount()��û���κ�Ч��
	 * @param from
	 *            ��ʼ����(����)��if(from<0) then from=0;
	 * @param to
	 *            ��������(������)��if(to>=getHeight()) then to=getHeight();
	 */
	public void fill(int mode, int row, float from, float to) {
		// check is mode supported
		Pair<Color, Color> colors = null;
		switch (mode) {
		case FORE_MODE:
			colors = this.fores;
			break;
		case BACK_MODE:
			colors = this.backs;
			break;
		default:
			return;
		}
		// check row bounds
		if (row >= this.getRowCount())
			return;
		// check from/to bounds
		from = this.checkBounds(from) + 1;
		to = this.checkBounds(to) + 1;
		if (from == to)
			return;
		// make sure that from < to
		if (to < from) {
			float temp = from;
			from = to;
			to = temp;
		}
		// we do the actually draw work here
		int totalRowHeight = rowHeight + rowGap + 2 * BORDER_SIZE;
		int y = row * totalRowHeight + 1;
		Rectangle2D.Float rect = new Rectangle2D.Float();
		rect.setRect(from, y, to - from, rowHeight);
		Graphics2D g2 = (Graphics2D) getImage().createGraphics();
		g2.setPaint(new GradientPaint(from, y, colors.first(), from, y + rowHeight, colors.second()));
		g2.fill(rect);
	}

	private float checkBounds(float index) {
		if (index < 0)
			return 0;
		int width = this.getWidth();
		if (index > width)
			return width;
		else
			return index;
	}

	private void initializeImage(BufferedImage img) {
		int height = img.getHeight();
		int width = img.getWidth();
		int totalRowHeight = rowHeight + rowGap + 2 * BORDER_SIZE;
		rowCount = height / totalRowHeight;
		Graphics2D g2 = (Graphics2D) img.createGraphics();
		// debug: fill background
		// g2.setColor( background );
		// g2.fillRect( 0, 0, width, height );
		// debug end
		Rectangle rect = new Rectangle();
		Paint back = new GradientPaint(0, 0, backs.first(), 0, rowHeight, backs.second());
		for (int row = 0; row < rowCount; row++) {
			// draw border
			g2.setColor(borderColor);
			rect.setBounds(0, 0, width - 1, rowHeight + 2 * BORDER_SIZE - 1);
			g2.draw(rect);
			// fill content
			g2.setPaint(back);
			rect.setBounds(1, 1, width - 2, rowHeight);
			g2.fill(rect);
			// draw gap
			g2.translate(0, rowHeight + 2 * BORDER_SIZE);
			rect.setBounds(0, 0, width, rowGap);
			g2.setColor(background);
			g2.fill(rect);
			g2.translate(0, rowGap);
		}
		// fill rest with background
		rect.setBounds(0, 0, width, height - rowCount * totalRowHeight);
		g2.setColor(background);
		g2.fill(rect);
		g2.dispose();
	}
}
